﻿using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Net;
using System.Net.Http;
using System.Net.Http.Json;
using Volo.Abp.DependencyInjection;

namespace FlyingEye.HttpServices
{
    public class HttpTokenManager : ISingletonDependency
    {
        private readonly IOptionsSnapshot<HttpClientServiceOptions> _options;
        private readonly ILogger<HttpTokenManager> _logger;

        /// <summary>
        /// 令牌是单例，所以应该考虑多线程问题
        /// </summary>
        private readonly SemaphoreSlim _tokenLock = new(1, 1);

        private DateTimeOffset? _expirationTime;
        private BearerHttpResponse? _bearer;
        private readonly IHttpClientFactory _httpClientFactory;

        public HttpTokenManager(
            IHttpClientFactory httpClientFactory,
            IOptionsSnapshot<HttpClientServiceOptions> options,
            ILogger<HttpTokenManager> logger)
        {
            _httpClientFactory = httpClientFactory;
            _options = options;
            _logger = logger;
        }

        private bool IsValidToken() =>
            _expirationTime != null && DateTimeOffset.UtcNow < _expirationTime;

        public async Task<BearerHttpResponse> GetBearerTokenAsync()
        {
            await _tokenLock.WaitAsync();

            try
            {
                if (IsValidToken()) return _bearer!;

                var requestContent = new FormUrlEncodedContent(new Dictionary<string, string>
            {
                {"client_id", _options.Value.ClientId },
                {"client_secret", _options.Value.ClientSecret },
                {"grant_type", _options.Value.GrantType }
            });

                using var httpClient = _httpClientFactory.CreateClient("HttpTokenClient");
                httpClient.Timeout = TimeSpan.FromSeconds(10);

                var response = await httpClient.PostAsync(_options.Value.AuthUrl, requestContent);
                response.EnsureSuccessStatusCode();

                _bearer = await response.Content.ReadFromJsonAsync<BearerHttpResponse>()
                    ?? throw new InvalidOperationException("Bearer token 反序列化失败");

                var bufferTime = 5;
                var expiresIn = Math.Max(_bearer.ExpiresIn - bufferTime, 0);
                _expirationTime = DateTimeOffset.UtcNow.AddSeconds(expiresIn);

                _logger.LogInformation("Token 已刷新，过期时间: {ExpirationTime}", _expirationTime);

                return _bearer;
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Token 获取失败");
                throw;
            }
            finally
            {
                _tokenLock.Release();
            }
        }
    }
}
